/**
 * 算术题的类
 */
package math;

import java.util.*;

public class MathProblem {
    //用于设定算术题的数字，从下标0开始从算术题的左至右排列
    private List<Float> numbers;
    //用于设定算术题的运算符，从下标0开始从算术题的左至右排列
    private List<MathMode> modes;
    //用于设定算术题的数字个数
    private int problemSize;
    //用于设定算术题可能的最大值与最小值
    private int max;
    private int min;
    //指定是否需要小数点题目
    private boolean needFloat;
    //代表本题目的答案
    private float result;
    private String problemText = "";
    /**
     * 构造函数
     * 传入算术题需要的数字个数，根据数字个数生成算术题
     *
     * @param problemSize 数字个数
     */
    protected MathProblem(int problemSize, int max, int min,boolean needFloat) {
        this.problemSize = problemSize;
        this.max = max;
        this.min = -min;
        this.numbers = new ArrayList<>();
        this.modes = new ArrayList<>();
        this.needFloat = needFloat;

        //使用随机数生成类Random构建numbers与mods
        Random random = new Random();
        for (int i = 0; i < this.problemSize; i++) {
            //在numbers中加入一个范围在min~max之间的随机数
            this.numbers.add(this.getRandomNumber(random, this.min, this.max));

            //在modes中加入随机的运算符
            //n个数只需要n-1个运算符
            if (i == this.problemSize - 1) continue;
            this.modes.add(this.getRandomMathMode(random));
        }
    }

    /**
     * 通过random返回一个随机数
     * @param random    Random对象
     * @param minNumber 最小值
     * @param maxNumber 最大值
     * @return 随机数
     */
    private float getRandomNumber(Random random, int minNumber, int maxNumber) {
        int bound = (minNumber < 0) ? (maxNumber - minNumber) : (maxNumber + minNumber);
        return (random.nextInt(bound) - minNumber);
    }

    /**
     *  运行当前的数学问题
     * @return 返回用户输入的结果
     */
    public float runMathProblem() {
        StringBuilder problemStr = new StringBuilder();
        for (int i = 0; i < this.getProblemSize(); i++) {
            //拼接数字
            int num = (int)this.getNumbers().get(i).floatValue();
            String numS = num + "";
            //如果数字为负数，需要加上括号
            if (this.getNumbers().get(i) < 0) {
                numS = "(" + numS + ")";
            }
            problemStr.append(numS);
            if (i == this.getProblemSize() - 1) problemStr.append("=?");
            else problemStr.append(this.getModes().get(i).getSign());
        }
        this.problemText = String.valueOf(problemStr);
        //接收用户输入的结果
        boolean eff = false;
        float userInput = 0;
        //使用try...catch防止用户输入了非float数据
        while (!eff){
            try {
                System.out.print(problemStr + "\n请回答：");
                Scanner sc = new Scanner(System.in);
                userInput = sc.nextFloat();
                eff = true;
            } catch (InputMismatchException e) {
                System.out.println("请输入纯数字！");
                eff = false;
            }
        }
        return userInput;
    }

    /**
     * 递归计算问题的答案
     * @param problem
     * @return
     */
    protected float getResultRecursion(MathProblem problem) {
        if (problem.modes.size() == 0) return problem.numbers.get(0);
        int processIndex = 0, x = 0, y = 0,modeIndex = 0;
        //如果符号的优先级为0，那么先寻找下一个符号
        if (problem.modes.get(0).getPriority() == 0) {
            processIndex = modeIndex+1;
            //如果下一个符号的优先级仍然为0或已经没有下一个符号，则应对modeIndex两侧的数进行运算
            //否则应对modeIndex+1两侧的数进行运算
            if (processIndex == problem.modes.size() || problem.modes.get(processIndex).getPriority() == 0)
                processIndex = modeIndex;
        }
        //开始运算并替换计算结果到processIndex左侧的数上
        x = processIndex;
        y = processIndex+1;
        problem.numbers.set(
                x,
                this.processByMode(
                        problem.getNumbers().get(x),
                        problem.getNumbers().get(y),
                        problem.modes.get(processIndex).getMode()
                )
        );
        problem.numbers.remove(y);
        problem.modes.remove(processIndex);
        return getResultRecursion(problem);
    }

    private float processByMode(float num1, float num2, MathMode.MathModeEnum mode) {
        switch (mode) {
            case Addition:
                return num1 + num2;
            case Subtraction:
                return num1 - num2;
            case Multiplication:
                return num1 * num2;
            case Division:
                return num1 / num2;
            default:
                return 0.0f;
        }
    }

    /**
     * 通过random构建一个随机数，再根据随机数返回对应的运算符
     * @param random Random对象
     * @return 随机运算符
     */
    private MathMode getRandomMathMode(Random random) {
        return PublicTools.ALL_MODE[random.nextInt(4)];
    }

    //====================================================================

    public List<Float> getNumbers() {
        return numbers;
    }

    public List<MathMode> getModes() {
        return modes;
    }

    public int getProblemSize() {
        return problemSize;
    }

    @Override
    public String toString() {
        return "MathProblem{" +
                "numbers=" + numbers +
                ", modes=" + modes +
                ", problemSize=" + problemSize +
                ", max=" + max +
                ", min=" + min +
                ", needFloat=" + needFloat +
                '}';
    }

    public MathProblem cloneThis(){
        MathProblem p = new MathProblem(this.problemSize, this.max, this.min, this.needFloat);
        p.modes = new ArrayList<>();
        p.modes.addAll(this.modes);
        p.numbers = new ArrayList<>();
        p.numbers.addAll(this.numbers);
        return p;
    }

    public void setResult(float result) {
        this.result = result;
    }

    public float getResult() {
        return result;
    }

    public String getProblemText() {
        return problemText;
    }

    public void setProblemText(String problemText) {
        this.problemText = problemText;
    }
}
